﻿#include <cmrc/cmrc.hpp>
#include <learn/gl/gl_shader.h>

// 声明资源命名空间
CMRC_DECLARE(Shader);

using namespace xi;
using namespace xi::gl;

GL_Shader::GL_Shader(const char *vertexPath, const char *fragmentPath, const char *geometryPath)
{
    unsigned int vertex = _load(vertexPath, GL_VERTEX_SHADER);
    unsigned int fragment = _load(fragmentPath, GL_FRAGMENT_SHADER);
    unsigned int geometry = 0;

    // shader Program
    m_ID = glCreateProgram();
    glAttachShader(m_ID, vertex);
    glAttachShader(m_ID, fragment);

    // 如果载入几何着色器
    if (geometryPath != nullptr)
    {
        geometry = _load(geometryPath, GL_GEOMETRY_SHADER);
        glAttachShader(m_ID, geometry);
    }

    glLinkProgram(m_ID);
    _check_compile_errors(m_ID, "PROGRAM");

    // 删除着色器
    glDeleteShader(vertex);
    glDeleteShader(fragment);

    if (geometryPath != nullptr)
        glDeleteShader(geometry);
}

void GL_Shader::enable()
{
    glUseProgram(m_ID);
}

GL_Shader::~GL_Shader()
{
    glDeleteProgram(m_ID);
}

unsigned int GL_Shader::_load(const char *path, int type)
{
    // 读取着色器内容
    auto fs = cmrc::Shader::get_filesystem();
    auto file = fs.open(path);

    // 得到字符串代码
    std::string code = std::string(file.begin(), file.end());
    const char *shaderCode = code.c_str();

    // 编译着色器
    unsigned int shader = glCreateShader(type);
    glShaderSource(shader, 1, &shaderCode, NULL);
    glCompileShader(shader);

    // 错误检查
    switch (type)
    {
    case GL_VERTEX_SHADER:
        _check_compile_errors(shader, "VERTEX");
        break;
    case GL_FRAGMENT_SHADER:
        _check_compile_errors(shader, "FRAGMENT");
        break;
    case GL_GEOMETRY_SHADER:
        _check_compile_errors(shader, "GEOMETRY");
        break;
    }

    return shader;
}

void GL_Shader::_check_compile_errors(unsigned int shader, std::string type)
{
    int success;
    char infoLog[1024];
    if (type != "PROGRAM")
    {
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(shader, 1024, NULL, infoLog);
            std::cerr << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n"
                      << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
    else
    {
        glGetProgramiv(shader, GL_LINK_STATUS, &success);
        if (!success)
        {
            glGetProgramInfoLog(shader, 1024, NULL, infoLog);
            std::cerr << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n"
                      << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
}
